package org.example.jnautil.mouse;

import basicutil.common.Point;
import basicutil.common.ThreadUtil;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.platform.win32.WinUser.HHOOK;
import com.sun.jna.platform.win32.WinUser.MSG;
import com.sun.jna.platform.win32.WinUser.MSLLHOOKSTRUCT;
import lombok.extern.slf4j.Slf4j;

import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.InputEvent;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
public class MouseManager {
    public static final MouseManager INSTANCE = new MouseManager();
    static final User32 user32 = User32.INSTANCE;
    static final WinDef.POINT curPos = new WinDef.POINT();
    static final HookMouseHandler hookMouseHandler = new HookMouseHandler();
    BlockingQueue<Point> mouseMoveQueue = new ArrayBlockingQueue<>(10);

    static Robot robot = null;


    static {


        try {
            robot = new Robot();
        } catch (AWTException e) {
            e.printStackTrace();
        }
    }

    private final StatelessMouseListener mouseMoveListener = new StatelessMouseListener() {
        @Override
        public void onMouse(MouseCodeName mouseCodeName, Point point) {
            if (!mouseMoveQueue.offer(point)) {
                log.debug("推送 鼠标 消息到队列失败，丢弃消息：{}", mouseCodeName);
            }
        }
    };


    private MouseManager() {
    }

    public synchronized BlockingQueue<Point> getMouseMoveQueue() {
        if (hookMouseHandler.mouseMoveListener == null) {
            hookMouseHandler.mouseMoveListener = mouseMoveListener;
        }
        return mouseMoveQueue;
    }

    public boolean isMouseMoveRunning() {
        return hookMouseHandler.mouseMoveListener != null;
    }

    public synchronized void delMouseMoveQueue() {
        hookMouseHandler.mouseMoveListener = null;
    }

    public static void drag(int x1, int y1, int x2, int y2) {

        Point location = INSTANCE.getLocation();
        robot.mouseMove(x1, y1);
        ThreadUtil.sleep(100);
        robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
        ThreadUtil.sleep(100);
        robot.mouseMove(x2, y2);
        ThreadUtil.sleep(100);
        robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
        ThreadUtil.sleep(100);
        robot.mouseMove(location.col, location.row);
    }

    // button: 0 鼠标左键，1 鼠标中键,，2 鼠标右键
    // pressType: 0 按下，1弹起，2点击
    public void click(int button, int pressType) {
        int mc = button == 0 ? InputEvent.BUTTON1_DOWN_MASK : (button == 1 ? InputEvent.BUTTON2_DOWN_MASK : InputEvent.BUTTON3_DOWN_MASK);
        if (pressType == 0) {
            robot.mousePress(mc);
        } else if (pressType == 1) {
            robot.mouseRelease(mc);
        } else {
            robot.mousePress(mc);
            robot.mouseRelease(mc);
        }
    }

    public Point getLocation() {

        user32.GetCursorPos(curPos);


        return Point.of(curPos.y, curPos.x);

    }

    public void move(int x, int y) {
        robot.mouseMove(x, y);
    }

    public void mouseLeftClick() {
        // 模拟鼠标按下操作
        INSTANCE.click(0, 2);

    }

    public void mouseRightClick() {
        // 模拟鼠标按下操作
        INSTANCE.click(2, 2);

    }

    public void mouseRightDown() {
        // 模拟鼠标按下操作
        INSTANCE.click(2, 0);

    }

    public void mouseRightUp() {
        // 模拟鼠标按下操作
        INSTANCE.click(2, 1);

    }

    public void mouseLeftClick(int x, int y) {
        robot.mouseMove(x, y);
        ThreadUtil.sleep(10);
        mouseLeftClick();

    }

    public void mouseLeftClick(int x, int y, int delay) {
        robot.mouseMove(x, y);
        ThreadUtil.sleep(10);
        mouseDown();
        ThreadUtil.sleep(delay);
        mouseUp();

    }

    public void mouseDown() {
        // 模拟鼠标按下操作
        robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);

    }

    public void mouseUp() {
        // 模拟鼠标释放操作
        robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);

    }

    public void startListenMouse() {
        new Thread(() -> {
            NativeMonitor.start(hookMouseHandler);
        }).start();
    }

    public void stopListenMouse() {
        NativeMonitor.stop();
    }

    public static void main(String[] args) {
        NativeMonitor.start(hookMouseHandler);
    }

    static class NativeMonitor {

        static WinDef.LRESULT not_pass = new WinDef.LRESULT(1);
        private static HHOOK hhk;
        static AtomicBoolean started = new AtomicBoolean();
        static ReentrantLock LOCK = new ReentrantLock();

        static void start(HookMouseHandler hookMouseHandler) {
            LOCK.lock();
            try {
                if (started.get()) {
                    return;
                }
                stop();
                started.set(true);
            } finally {
                LOCK.unlock();
            }

            User32.LowLevelMouseProc mouseHook;
            mouseHook = new User32.LowLevelMouseProc() {
                public WinDef.LRESULT callback(int nCode, WinDef.WPARAM wParam, MSLLHOOKSTRUCT info) {

                    if (nCode >= 0) {
                        boolean b = hookMouseHandler.acceptMouse(wParam.intValue(), info.pt.x, info.pt.y);
                        if (b) {
                            return user32.CallNextHookEx(hhk, nCode, wParam, new WinDef.LPARAM(Pointer.nativeValue(info.getPointer())));
                        } else {
                            return not_pass;
                        }
//                        MouseCodeName mouseCodeName = MouseCodeName.byCode(wParam.intValue());
//                        if (mouseCodeName != MouseCodeName.MOUSEMOVE) {
//                            // info.mouseData 里面可以表示向上滚动还是向下滚动
//                            System.out.println(nCode + ">0  " + wParam.intValue() + "  name:  " + mouseCodeName
//                                    +" , info x:"+info.pt.x+" ,y:"+info.pt.y+", info"+info+" ncode:" +nCode);
//                        }

                    }
                    return user32.CallNextHookEx(hhk, nCode, wParam, new WinDef.LPARAM(Pointer.nativeValue(info.getPointer())));
                }
            };
            WinDef.HMODULE hMod = Kernel32.INSTANCE.GetModuleHandle(null);
            hhk = user32.SetWindowsHookEx(WinUser.WH_MOUSE_LL, mouseHook, hMod, 0);

            int result;
            MSG msg = new MSG();
            while ((result = user32.GetMessage(msg, null, 0, 0)) != 0) {
                if (result == -1) {
                    System.err.println("error in get message");
                    break;
                } else {
                    System.err.println("got message");
                    user32.TranslateMessage(msg);
                    user32.DispatchMessage(msg);
                }
            }
        }


        static void stop() {
            started.set(false);
            if (hhk != null) {
                user32.UnhookWindowsHookEx(hhk);
            }
        }
    }

    static class NativeSender {
        public static void click() {
//user32.PostMessage(null,514,);
//user32.SetCursorPos()
        }

    }


}
